package com.lagou.edu.factory;


import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class AnnotationBeanFactory extends AbstractBeanFactory{

    List<String> classPaths = new ArrayList<>();

    private static AnnotationBeanFactory annotationBeanFactory = new AnnotationBeanFactory();

    public static AnnotationBeanFactory newInstance() {
        return annotationBeanFactory;
    }

    public void createSingleTonObjects() throws Exception {
        //包名
        String basePack = "com.lagou.edu";
        //先把包名转换为路径,首先得到项目的classpath
        String classpath = AnnotationBeanFactory.class.getResource("/").getPath();
        //然后把我们的包名basPach转换为路径名
        basePack = basePack.replace(".", "/");
        //然后把classpath和basePack合并
        String searchPath = classpath + basePack;
        doPath(new File(searchPath));
        //这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象
        for (String s : classPaths) {
            //把文件系统的绝对路径转换为全类名com.baibin.search.a.A
            s = s.replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class","");
            Class cls = Class.forName(s);

            //判断类上是否添加@Service注解
            Annotation annotation = cls.getAnnotation(Service.class);
            if (annotation instanceof Service){
                String beanId = ((Service) annotation).value() == null || "".equals(((Service) annotation).value()) ? cls.getSimpleName() : ((Service) annotation).value();

                Constructor constructor = cls.getConstructor();
                Object o = constructor.newInstance();
                //判断属性上是否添加@Autowired注解
                Field[] fields = cls.getDeclaredFields();
                List<Field> unSetFields = new ArrayList<>();
                Boolean fieldAllInstance = true;
                for (Field field : fields){
                    Annotation fAnnotation = field.getAnnotation(Autowired.class);
                    if(fAnnotation instanceof Autowired){
                        Object fObject = null;
                        if(((Autowired) fAnnotation).value() != null && ((Autowired) fAnnotation).value() != ""){//Autowired指定名称时，直接根据名称获取注入的bean对象
                            fObject = getBean(((Autowired) fAnnotation).value());
                        }else{//Autowired未指定名称时
                            if(field.getType().isInterface()){//如果为接口类，则注入实现类对象，需从容器中获取实现类对象
                                //获取实现类
                                Class implClass = getImplClass(classPaths, classpath, field.getType());
                                fObject = getBean(implClass);
                            }else{//如果未实现类，直接根据类型获取注入的bean对象
                                fObject = getBean(field.getType());
                            }
                        }

                        if(fObject != null){//属性需要注入的对象已经实例化
                            field.setAccessible(true);
                            field.set(o, fObject);
                        }else{//属性需要注入的对象未实例化,将当前对象放置缓存区，待注入对象实例化后再设置属性值
                            fieldAllInstance = false;
                            unSetFields.add(field);
                        }
                    }
                }

                if(fieldAllInstance){
                    setBean(beanId, o);
                }else{
                    setEarlySingletonObjects(beanId, o, unSetFields);
                }
            }
        }
    }

    public void clearEarlySingletonObjects() throws Exception {
        String classpath = AnnotationBeanFactory.class.getResource("/").getPath();
        List<EarlySingletonObject> earlySingletonObjects = getEarlySingletonObjects();//获取未初始化完成的bean
        if(!earlySingletonObjects.isEmpty()){
            Iterator<EarlySingletonObject> it = earlySingletonObjects.iterator();
            while (it.hasNext()){
                EarlySingletonObject earlySingletonObject = it.next();
                String id = earlySingletonObject.getId();//获取bean Id
                Object o = earlySingletonObject.getObject();//获取bean 实例
                List<Field> fields = earlySingletonObject.getFields();//获取bean中未初始化的属性
                Iterator<Field> fIt = fields.iterator();
                while (fIt.hasNext()){
                    Field field = fIt.next();
                    Object fObject = null;

                    if(field.getType().isInterface()){//如果为接口类，则注入实现类对象，需从容器中获取实现类对象
                        //获取实现类
                        Class implClass = getImplClass(classPaths, classpath, field.getType());
                        fObject = getBean(implClass);
                    }else{
                        fObject = getBean(field.getType());
                    }

                    if(fObject != null) {
                        field.setAccessible(true);
                        field.set(o, fObject);//设置bean实例的属性值
                        fIt.remove();//清除未初始化属性缓存
                    }
                }
                if(fields.size() == 0){//清除缓存中当前bean的全部未初始化属性后，再清除当前bean
                    it.remove();
                    setBean(id, o);//将初始化完成的bean转移至singletonObjects
                }
            }
        }
        if(earlySingletonObjects.size() > 0){
            clearEarlySingletonObjects();
        }
    }

    @Override
    protected void replaceProxyObjects() {
        Map<String,Object> singletonObjects = getSingletonObjects();
        Iterator<Map.Entry<String, Object>> iterator = singletonObjects.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<String, Object> entry = iterator.next();
            Object o = entry.getValue();
            String id = entry.getKey();
            Class cls = o.getClass();
            Method[] methods = cls.getDeclaredMethods();
            for(Method method : methods){
                if(method.getAnnotation(Transactional.class) instanceof Transactional){//存在添加@Transactional注解的方法
                    ProxyFactory proxyFactory = (ProxyFactory) getBean(ProxyFactory.class);
                    setBean(id, proxyFactory.getCglibProxy(o));
                    break;
                }
            }
        }
    }

    private Class getImplClass(List<String> classPaths, String classpath, Class<?> type) throws ClassNotFoundException {
        for(String s : classPaths){
            s = s.replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class","");
            Class cls = Class.forName(s);
            for(Class interfaceClass : cls.getInterfaces()){
                if(type.equals(interfaceClass)){
                    return cls;
                }
            }
        }
        return null;
    }


    /**
     * 该方法会得到所有的类，将类的绝对路径写入到classPaths中
     * @param file
     */
    private void doPath(File file) {
        if (file.isDirectory()) {//文件夹
            //文件夹我们就递归
            File[] files = file.listFiles();
            for (File f1 : files) {
                doPath(f1);
            }
        } else {//标准文件
            //标准文件我们就判断是否是class文件
            if (file.getName().endsWith(".class")) {
                //如果是class文件我们就放入我们的集合中。
                classPaths.add(file.getPath());
            }
        }
    }

}
